/**
 * Project  : Looker
 * Package  : net.freestu.looker.monitor
 * File     : MonitorScheduler.java
 * Author   : solosky < solosky772@qq.com >
 * Date     : Aug 27, 2009
 * Modified : Aug 27, 2009
 * License  : Apache License 2.0 
 */
package net.freestu.looker.monitor;

import java.util.Comparator;
import java.util.Iterator;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.Semaphore;

import org.apache.log4j.Logger;

import net.freestu.looker.common.Category;
import net.freestu.looker.common.Looker;
import net.freestu.looker.common.Site;
import net.freestu.looker.common.SiteManager;


/**
 *  新闻更新或检查计划器
 *  启动和管理更新或检查
 */
public class MonitorScheduler
{
	/**
	 * 检查器
	 */
	public static int MONITOR_LISTMONITOR = 1;
	
	/**
	 * 更新器
	 */
	public static int MONITOR_CONTENTUPDATER = 2;
	/**
	 * 监视类型
	 */
	private int monitorType;
	/**
	 * 检查点队列
	 */
	private PriorityBlockingQueue<CheckPoint> checkPointQueue;
	
	/**
	 * 允许同时运行的监视器数目
	 */
	private Semaphore activeMonitorSemaphore;
	
	/**
	 * 自运行线程
	 */
	private Thread innerThread;
	
	/**
	 * 日志记录
	 */
	private static Logger logger = Logger.getLogger(MonitorScheduler.class);
	
	/**
	 * 构造函数
	 * @param monitorType
	 */
	public MonitorScheduler(int monitorType,int maxActiveMonitor)
	{
		this.monitorType = monitorType;
		this.checkPointQueue = new PriorityBlockingQueue<CheckPoint>(10, new CheckTimeComparator());
		this.activeMonitorSemaphore = new Semaphore(maxActiveMonitor);
	}

	/**
	 * 初始化函数
	 * 添加第一次检查点
	 */
	public void init()
	{
		Iterator<Site>  sit = SiteManager.getInstance().getSiteList().iterator();
		while(sit.hasNext()) {
			Site site = sit.next();
			Iterator<Category> cit = site.getCategoryList().iterator();
			while(cit.hasNext()) {
				finish(cit.next());
			}
		}
	}
	
	/**
	 * 开始监视
	 */
	public void start()
	{
		Runnable r = new Runnable(){
			public void run()
			{
				try {
	                scheduling();
                } catch (InterruptedException e) {
	              logger.warn(monitorType==MONITOR_LISTMONITOR?"列表监视器":"新闻更新器"+"计划器被中断。");
                }
			}
		};
		innerThread = new Thread(r);
		innerThread.setName(monitorType==MONITOR_LISTMONITOR?"ListMonitorScheduler":"ContentMonitorScheduler");
		innerThread.start();
		
	}
	
	/**
	 * 停止监视
	 */
	public void stop()
	{
		innerThread.interrupt();
	}
	
	
	/**
	 * 计划器主要的函数
	 * 每隔固定的时间醒来检查工作点队列，如果当前队列头的工作点小于当前时间，执行相应的任务
	 * @throws InterruptedException 
	 */
	private void scheduling() throws InterruptedException
	{
		// 反复循环，直到
		while(true)
		{
			// 每次醒来，检查队列的头元素
			// 如果没有在此等待,如果有检查第一个元素，如果小于或者等于当前时间，就从工作池中分配任务
			CheckPoint curCheck = null;
			while (checkPointQueue.peek()!=null && checkPointQueue.peek().getChecktime()<System.currentTimeMillis())
			{
				curCheck = checkPointQueue.remove();
				//获取是否可以运行的许可,如果超过正在运行检查器的数目，则等待
				activeMonitorSemaphore.acquire();
				//建立检查器，并运行
				AbstractMonitor monitor = null;
				if(monitorType==MONITOR_LISTMONITOR)
					monitor = new ListMonitor(curCheck.getCategory());
				else
					monitor = new ContentUpdater(curCheck.getCategory());
				
				//交给线程池执行
				Looker.executors.submit(monitor);
			}
			
			//休眠指定的时间，这里默认为1分钟
			Thread.sleep(1000*60);
		}
	}
	
	
	/**
	 * 完成后的回调函数，由检查器或者监视器调用
	 * @param category
	 */
	public void finish(Category category)
	{
		//释放许可
		activeMonitorSemaphore.release();
		//添加下一次检查点
		long checktime = (monitorType==MONITOR_LISTMONITOR)
						? category.getConfiguration().getLong("monitorInterval")
						: category.getConfiguration().getLong("updaterInterval");
		checkPointQueue.add(new CheckPoint(category, checktime*60));
	}
	
	
	/**
	 * 排序的实现
	 */
	private class CheckTimeComparator implements Comparator<CheckPoint>
	{

		/* (non-Javadoc)
		 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
		 */
		@Override
		public int compare(CheckPoint c1, CheckPoint c2)
		{
			if(c1.getChecktime()<c2.getChecktime())
				return -1;
			else if(c1.getChecktime()>c2.getChecktime())
				return 1;
			else
				return 0;
		}	
	}
	
}
